package stdlog

import (
	"fmt"
	"log"
	"os"
	"slices"

	"gitee.com/hasika/gotool/logx/logdef"
)

type field struct {
	K string
	V interface{}
}

type Logger struct {
	skip   int
	fields []field
	level  logdef.Level
	logger *log.Logger
	name   string
}

func NewDefaultLog() *Logger {
	lg := log.New(os.Stdout, "", log.Lshortfile|log.LstdFlags|log.Lmicroseconds)
	l := &Logger{
		logger: lg,
		skip:   3,
	}
	l.level = logdef.LevelInfo
	l.updatePrefix()
	return l
}

func NewLog(lg *log.Logger) *Logger {
	return &Logger{
		logger: lg,
	}
}

func (l Logger) Debug(args ...interface{}) {
	l.print(logdef.LevelDebug, fmt.Sprint(args...))
}

func (l Logger) Debugf(format string, args ...interface{}) {
	l.print(logdef.LevelDebug, fmt.Sprintf(format, args...))
}

func (l Logger) Info(args ...interface{}) {
	l.print(logdef.LevelInfo, fmt.Sprint(args...))
}

func (l Logger) Infof(format string, args ...interface{}) {
	l.print(logdef.LevelInfo, fmt.Sprintf(format, args...))
}

func (l Logger) Warn(args ...interface{}) {
	l.print(logdef.LevelWarn, fmt.Sprint(args...))
}

func (l Logger) Warnf(format string, args ...interface{}) {
	l.print(logdef.LevelWarn, fmt.Sprintf(format, args...))
}

func (l Logger) Error(args ...interface{}) {
	l.print(logdef.LevelError, fmt.Sprint(args...))
}

func (l Logger) Errorf(format string, args ...interface{}) {
	l.print(logdef.LevelError, fmt.Sprintf(format, args...))
}

func (l Logger) Fatal(v ...interface{}) {
	l.print(logdef.LevelFatal, fmt.Sprint(v...))
	os.Exit(1)
}

func (l Logger) Fatalf(format string, v ...interface{}) {
	l.print(logdef.LevelFatal, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func (l Logger) Panic(v ...interface{}) {
	l.print(logdef.LevelPanic, fmt.Sprint(v...))
}

func (l Logger) Panicf(format string, v ...interface{}) {
	l.print(logdef.LevelPanic, fmt.Sprintf(format, v...))
}

func (l Logger) WithField(key string, value interface{}) logdef.ILogger {
	c := l.fastClone()
	c.fields = slices.DeleteFunc(c.fields, func(f field) bool {
		return f.K == key
	})
	c.fields = append(c.fields, field{K: key, V: value})
	return c
}

func (l Logger) WithFields(fields logdef.Fields) logdef.ILogger {
	c := l.fastClone()
	for key, value := range fields {
		c.fields = slices.DeleteFunc(c.fields, func(f field) bool {
			return f.K == key
		})
		c.fields = append(c.fields, field{K: key, V: value})
	}
	return c
}

func (l Logger) WithSkip(skip int) logdef.ILogger {
	c := l.fastClone()
	c.skip += skip
	return c
}

func (l Logger) WithLevel(level logdef.Level) logdef.ILogger {
	c := l.deepClone()
	c.level = level
	c.updatePrefix()
	return c
}

func (l Logger) WithName(name string) logdef.ILogger {
	c := l.deepClone()
	c.name = name
	c.updatePrefix()
	return c
}

func (l Logger) updatePrefix() {
	l.logger.SetPrefix(fmt.Sprintf("%s %s ", l.level, l.name))
}

func (l Logger) fastClone() Logger {
	c := Logger{
		skip:   l.skip,
		level:  l.level,
		logger: l.logger,
		name:   l.name,
	}
	c.fields = make([]field, len(l.fields))
	copy(c.fields, l.fields)
	return c
}

func (l Logger) deepClone() Logger {
	c := Logger{
		skip:   l.skip,
		level:  l.level,
		logger: log.New(l.logger.Writer(), l.logger.Prefix(), l.logger.Flags()),
		name:   l.name,
	}
	for _, fd := range l.fields {
		c.fields = append(c.fields, field{K: fd.K, V: fd.V})
	}
	return c
}

func (l Logger) print(level logdef.Level, msg string) {
	if level.IntValue() < l.level.IntValue() {
		return
	}
	fsl, cnt := l.fields, len(l.fields)
	content := make([]interface{}, 0, cnt*4+2)
	for _, f := range fsl {
		content = append(content, " ", f.K, " ", f.V)
	}
	content = append(content, " ", msg)
	l.logger.Output(l.skip, fmt.Sprint(content...))
}
